<template>
  <div class="voice-transcriber">
    <el-card class="transcriber-card" shadow="hover">
      <template #header>
        <div class="card-header">
          <span class="card-title">
            <el-icon><Microphone /></el-icon>
            语音转写
          </span>
          <div class="status-indicator" :class="statusClass">
            {{ statusText }}
          </div>
        </div>
      </template>

      <div class="transcriber-content">
        <!-- 控制按钮 -->
        <div class="control-buttons">
          <el-button
            v-if="!isRecording"
            type="success"
            icon="Microphone"
            @click="startRecording"
            :disabled="!isTranscriptionStarted"
          >
            开始录音
          </el-button>
          
          <el-button
            v-if="isRecording"
            type="danger"
            icon="VideoPause"
            @click="stopRecording"
          >
            停止录音
          </el-button>

          <el-button
            type="warning"
            @click="clearResults"
          >
            清空结果
          </el-button>
        </div>

        <!-- 录音状态显示 -->
        <div v-if="isRecording" class="recording-status">
          <div class="recording-indicator">
            <div class="pulse-dot"></div>
            <span>正在录音中...</span>
            <span class="duration">{{ formatDuration(recordingDuration) }}</span>
          </div>
        </div>

        <!-- 错误信息 -->
        <div v-if="errorMessage" class="error-message">
          <el-alert
            :title="errorMessage"
            type="error"
            :closable="false"
            show-icon
          />
        </div>

        <!-- 完整文本输出 -->
        <div class="full-text">
          <h4>转录文本：</h4>
          <el-input
            v-model="fullText"
            type="textarea"
            :rows="6"
            placeholder="转录文本将显示在这里..."
            readonly
            class="transcription-textarea"
          />
          <div class="text-actions">
            <el-button size="small" @click="copyToClipboard">复制文本</el-button>
          </div>
        </div>
      </div>
    </el-card>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import { Microphone, VideoPause } from '@element-plus/icons-vue'

// 接口定义
interface WebSocketMessage {
  header: {
    name: string
    message_id: string
    task_id: string
    namespace: string
    appkey?: string
  }
  payload: any
}

interface Props {
  websocketUrl?: string
  appkey?: string
  token?: string
  maxDuration?: number
}

const props = withDefaults(defineProps<Props>(), {
  websocketUrl: 'wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1',
  appkey: 'default',
  token: 'default',
  maxDuration: 300
})

const emit = defineEmits<{
  'transcription-update': [text: string]
}>()

// 响应式状态
const isRecording = ref(false)
const isConnecting = ref(false)
const isSupported = ref(true)
const isConnected = ref(false)
const isTranscriptionStarted = ref(false)
const fullText = ref('')
const errorMessage = ref('')
const recordingDuration = ref(0)

// 内部状态
let websocket: WebSocket | null = null
let audioStream: MediaStream | null = null
let audioContext: AudioContext | null = null
let scriptProcessor: ScriptProcessorNode | null = null
let audioInput: MediaStreamAudioSourceNode | null = null
let durationTimer: number | null = null
let startTime: number = 0
let taskId: string = ''

// 计算属性
const statusClass = computed(() => {
  if (isRecording.value) return 'recording'
  if (isConnecting.value) return 'connecting'
  if (isConnected.value) return 'connected'
  return 'disconnected'
})

const statusText = computed(() => {
  if (isConnecting.value) return '连接中...'
  if (isRecording.value) return '录音中'
  if (isConnected.value) return '已连接'
  return '未连接'
})

// 工具函数
const generateUUID = (): string => {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c: string) => {
    const r = Math.random() * 16 | 0
    const v = c === 'x' ? r : (r & 0x3 | 0x8)
    return v.toString(16)
  })
}

// 检查浏览器支持
const checkSupport = () => {
  if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
    isSupported.value = false
    errorMessage.value = '您的浏览器不支持录音功能'
    return false
  }
  
  if (!window.AudioContext && !(window as any).webkitAudioContext) {
    isSupported.value = false
    errorMessage.value = '您的浏览器不支持 Web Audio API'
    return false
  }
  
  return true
}

// 连接 WebSocket
const connectWebSocket = async () => {
  if (!checkSupport()) return
  
  try {
    isConnecting.value = true
    errorMessage.value = ''
    
    const socketUrl = `${props.websocketUrl}?token=${props.token}`
    websocket = new WebSocket(socketUrl)
    
    websocket.onopen = () => {
      isConnected.value = true
      isConnecting.value = false
      taskId = generateUUID()
      
      // 发送开始转录消息
      const startTranscriptionMessage = {
        header: {
          appkey: props.appkey,
          namespace: "SpeechTranscriber",
          name: "StartTranscription",
          task_id: taskId,
          message_id: generateUUID()
        },
        payload: {
          format: "pcm",
          sample_rate: 16000,
          enable_intermediate_result: true,
          enable_punctuation_prediction: true,
          enable_inverse_text_normalization: true
        }
      }
      
      websocket!.send(JSON.stringify(startTranscriptionMessage))
    }
    
    websocket.onmessage = (event) => {
      try {
        const message: WebSocketMessage = JSON.parse(event.data)
        handleWebSocketMessage(message)
      } catch (error) {
        console.error('解析 WebSocket 消息失败:', error)
      }
    }
    
    websocket.onerror = (error) => {
      console.error('WebSocket 错误:', error)
      errorMessage.value = '语音识别服务连接失败'
      isConnecting.value = false
    }
    
    websocket.onclose = () => {
      isConnected.value = false
      isTranscriptionStarted.value = false
    }
    
  } catch (error: any) {
    console.error('连接失败:', error)
    errorMessage.value = `连接失败: ${error.message}`
    isConnecting.value = false
  }
}

// 处理 WebSocket 消息
const handleWebSocketMessage = (message: WebSocketMessage) => {
  switch (message.header.name) {
    case 'TranscriptionStarted':
      isTranscriptionStarted.value = true
      break
    case 'TranscriptionResultChanged':
      updateTranscription(message.payload, false) // 中间结果
      break
    case 'SentenceEnd':
      updateTranscription(message.payload, true) // 最终结果
      break
    case 'TaskFailed':
      console.error('任务失败:', message.payload)
      errorMessage.value = `识别任务失败: ${message.payload.message || '未知错误'}`
      break
  }
}

// 更新转录结果
const updateTranscription = (payload: any, isFinal: boolean = false) => {
  // 从实际服务端消息中提取文本
  const text = payload.result || payload.text || ''
  
  if (text) {
    if (isFinal) {
      // 最终结果，添加到完整文本
      fullText.value += (fullText.value ? ' ' : '') + text
      emit('transcription-update', fullText.value)
    } else {
      // 临时结果，可以在这里处理中间结果显示
      // 当前简化版本只显示最终结果
    }
  }
}

// 开始录音
const startRecording = async () => {
  if (!isTranscriptionStarted.value) {
    ElMessage.warning('请等待服务连接完成')
    return
  }
  
  try {
    // 获取音频输入设备
    audioStream = await navigator.mediaDevices.getUserMedia({ audio: true })
    
    // 创建音频上下文
    const AudioContextClass = window.AudioContext || (window as any).webkitAudioContext
    audioContext = new AudioContextClass({
      sampleRate: 16000
    })
    audioInput = audioContext.createMediaStreamSource(audioStream)
    
    // 设置脚本处理器
    scriptProcessor = audioContext.createScriptProcessor(2048, 1, 1)
    
    scriptProcessor.onaudioprocess = (event) => {
      const inputData = event.inputBuffer.getChannelData(0)
      const inputData16 = new Int16Array(inputData.length)
      
      // 转换为16位PCM
      for (let i = 0; i < inputData.length; ++i) {
        const sample = inputData[i] || 0
        inputData16[i] = Math.max(-1, Math.min(1, sample)) * 0x7FFF // PCM 16-bit
      }
      
      if (websocket && websocket.readyState === WebSocket.OPEN) {
        websocket.send(inputData16.buffer)
      }
    }
    
    audioInput.connect(scriptProcessor)
    scriptProcessor.connect(audioContext.destination)
    
    isRecording.value = true
    startTime = Date.now()
    startDurationTimer()
    
    ElMessage.success('开始录音')
  } catch (error: any) {
    console.error('录音启动失败:', error)
    errorMessage.value = `录音启动失败: ${error.message}`
    ElMessage.error('录音启动失败')
  }
}

// 停止录音
const stopRecording = () => {
  try {
    if (scriptProcessor) {
      scriptProcessor.disconnect()
      scriptProcessor = null
    }
    if (audioInput) {
      audioInput.disconnect()
      audioInput = null
    }
    if (audioStream) {
      audioStream.getTracks().forEach(track => track.stop())
      audioStream = null
    }
    if (audioContext) {
      audioContext.close()
      audioContext = null
    }
    
    isRecording.value = false
    stopDurationTimer()
    
    ElMessage.success('录音已停止')
  } catch (error) {
    console.error('停止录音时出错:', error)
    ElMessage.error('停止录音时出错')
  }
}

// 清空结果
const clearResults = () => {
  fullText.value = ''
  emit('transcription-update', '')
  ElMessage.success('结果已清空')
}

// 复制到剪贴板
const copyToClipboard = async () => {
  try {
    await navigator.clipboard.writeText(fullText.value)
    ElMessage.success('文本已复制到剪贴板')
  } catch (error) {
    ElMessage.error('复制失败')
  }
}

// 开始计时
const startDurationTimer = () => {
  durationTimer = window.setInterval(() => {
    recordingDuration.value = Math.floor((Date.now() - startTime) / 1000)
    
    // 检查最大录音时长
    if (recordingDuration.value >= props.maxDuration) {
      ElMessage.warning('已达到最大录音时长，自动停止录音')
      stopRecording()
    }
  }, 1000)
}

// 停止计时
const stopDurationTimer = () => {
  if (durationTimer) {
    clearInterval(durationTimer)
    durationTimer = null
  }
}

// 格式化时长
const formatDuration = (seconds: number) => {
  const mins = Math.floor(seconds / 60)
  const secs = seconds % 60
  return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
}

// 清理资源
const cleanup = () => {
  stopRecording()
  
  if (websocket) {
    websocket.close()
    websocket = null
  }
  
  isConnected.value = false
  isTranscriptionStarted.value = false
  isRecording.value = false
}

// 组件挂载时自动连接
onMounted(() => {
  if (checkSupport()) {
    connectWebSocket()
  }
})

// 组件卸载
onUnmounted(() => {
  cleanup()
})
</script>

<style scoped>
.voice-transcriber {
  margin-bottom: 2rem;
}

.transcriber-card {
  border-radius: 12px;
  overflow: hidden;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.card-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 1.25rem;
  font-weight: 600;
  color: #303133;
}

.status-indicator {
  padding: 4px 12px;
  border-radius: 20px;
  font-size: 0.875rem;
  font-weight: 500;
  transition: all 0.3s ease;
}

.status-indicator.disconnected {
  background: #f0f0f0;
  color: #666;
}

.status-indicator.connecting {
  background: #e3f2fd;
  color: #1976d2;
}

.status-indicator.connected {
  background: #e8f5e8;
  color: #2e7d32;
}

.status-indicator.recording {
  background: #ffebee;
  color: #c62828;
  animation: pulse 1.5s infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

.transcriber-content {
  padding: 1rem 0;
}

.control-buttons {
  display: flex;
  gap: 12px;
  margin-bottom: 1.5rem;
  flex-wrap: wrap;
}

.recording-status {
  margin-bottom: 1.5rem;
}

.recording-indicator {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  background: #ffebee;
  border-radius: 8px;
  color: #c62828;
  font-weight: 500;
}

.pulse-dot {
  width: 12px;
  height: 12px;
  background: #c62828;
  border-radius: 50%;
  animation: pulse 1.5s infinite;
}

.duration {
  margin-left: auto;
  font-family: 'Courier New', monospace;
  font-weight: bold;
}

.error-message {
  margin-bottom: 1.5rem;
}

.full-text {
  margin-bottom: 1.5rem;
}

.full-text h4 {
  margin: 0 0 1rem 0;
  color: #303133;
  font-size: 1rem;
}

.transcription-textarea {
  margin-bottom: 12px;
}

.text-actions {
  display: flex;
  gap: 8px;
}

/* 响应式设计 */
@media (max-width: 768px) {
  .control-buttons {
    flex-direction: column;
  }
  
  .control-buttons .el-button {
    width: 100%;
  }
  
  .text-actions {
    flex-direction: column;
  }
}
</style>
